﻿/** 
Copyright (c) 2010, Sheikh Abdul Wahid Ahmed
Details @ http://extremecodeworld.codeplex.com/license
**/

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Threading;


namespace EXtremecode.Common
{
    public class ObjectPool
    {
        private Stack<IPooledObject> pool = new Stack<IPooledObject>();
        private int poolSize;
        private int numberOfPooledObjectsCreated = 0;
        private CreateObjectDelegate<IPooledObject> FPCreateObject;
        private AutoResetEvent autoReset = new AutoResetEvent(false);
        private string poolName = string.Empty;

        public ObjectPool(CreateObjectDelegate<IPooledObject> FPCreateObject)
            : this("UnKnown",FPCreateObject, 10)
        {}

        public ObjectPool(string poolName, CreateObjectDelegate<IPooledObject> FPCreateObject)
            : this(poolName,FPCreateObject, 10)
        { }

        public ObjectPool(string poolName,CreateObjectDelegate<IPooledObject> FPCreateObject,int poolSize)
        {
            this.FPCreateObject = FPCreateObject;
            this.poolSize = poolSize;
            this.poolName = poolName;
        }

        public IPooledObject GetObject()
        {
            while (true)
            {
                lock (pool)
                {
                    if (pool.Count > 0)
                    {
                        //retrun object from the pool.
                        Console.WriteLine("[{0}] PooledObject Poped  (Opened Objects [{1}])"
                            , poolName
                            ,numberOfPooledObjectsCreated - pool.Count);
                        
                        return pool.Pop();
                        

                    }
                    else
                    {
                        if (numberOfPooledObjectsCreated < poolSize)
                        {
                            //numbers of pooled object has not crossed the limit yet
                            //so create new object and return
                            IPooledObject pooledObject = FPCreateObject();
                            if (pooledObject != null)
                            {
                                numberOfPooledObjectsCreated++;
                                pooledObject.Pool = this; //add self reference
                                //which will be used to get the object back into pool again. 
                            }
                            Console.WriteLine("[{0}] PooledObject Created  (Opened Objects [{1}])"
                                , poolName
                                , numberOfPooledObjectsCreated - pool.Count);

                            return pooledObject;
                            
                            
                        }
                    }

                    //it also possible that other thread set the signal
                    //before conrol reaching on this statement.
                    //for making sure that thread will not stay here indefinitly,
                    //we are awaking it after given period of time.

                    Console.WriteLine("[{0}] Waiting for PooledObject", poolName);
                    autoReset.WaitOne(1000);
                }
               
                
            }
            
            
        }

        [System.Runtime.CompilerServices.MethodImpl
        (System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
        public void ReleaseObject(IPooledObject pooledObject)
        {
            pool.Push(pooledObject);
            autoReset.Set();
            Console.WriteLine("[{0}] PooledObject Pushed  (Opened Objects [{1}])"
                , poolName
                , numberOfPooledObjectsCreated - pool.Count);
        }
        
    }

    
}
